package google.sites.view;

import com.google.gdata.data.sites.ContentFeed;
import com.google.gdata.data.sites.WebPageEntry;
import com.google.gdata.util.ServiceException;
import dev.tools.design.patterns.command.Command;
import dev.tools.design.patterns.state.IStateHandler;
import dev.tools.design.patterns.state.StateChanged;
import google.sites.model.Kind;
import google.sites.model.Model;
import google.sites.view.dialogs.CreateWebPageDialog;
import google.sites.view.dialogs.DeleteWebPageDialog;
import google.sites.view.dialogs.RenameWebPageDialog;

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Created with IntelliJ IDEA.
 * User: Nuno
 * Date: 04-10-2013
 * Time: 17:31
 */
public class Tree extends JTree implements IStateHandler<Model, Model.State> {
    private static final Map<String, List<Node>> waiting = new LinkedHashMap<>();
    private final Map<String, Node> nodes = new LinkedHashMap<>();
    private final DefaultMutableTreeNode root;
    private final DefaultTreeModel model;

    public Tree() {
        root = new DefaultMutableTreeNode("root");
        model = new DefaultTreeModel(root);
        setModel(model);
        setEnabled(false);
        setRootVisible(false);

        Model.INSTANCE.addHandler(this, Model.LOGGED_IN);

        addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                if (SwingUtilities.isRightMouseButton(e)) {
                    setSelectionRow(getClosestRowForLocation(e.getX(), e.getY()));

                    TreePath path = getPathForLocation(e.getX(), e.getY());
                    assert path != null;
                    final Node node = (Node) path.getLastPathComponent();

                    new ContextMenu(node).show(Tree.this, e.getX(), e.getY());
                }
            }
        });
    }

    @Override
    public void update(Model context, StateChanged stateChanged) {
        setEnabled(true);
        try {
            String url = Model.INSTANCE.getContentFeedUrl() + "?kind=" + Kind.webpage;
            ContentFeed contentFeed = Model.INSTANCE.service.getFeed(new URL(url), ContentFeed.class);

            root.removeAllChildren();

            for (WebPageEntry entry : contentFeed.getEntries(WebPageEntry.class))
                new Node(entry);

            expandPath(new TreePath(root.getPath()));

        } catch (IOException | ServiceException e) {
            View.showError(e);
        }
    }

    public class ContextMenu extends JPopupMenu {
        public ContextMenu(final Node node) {
            add(new CreateWebPageDialog(node.id).onCommandFinished(new IStateHandler<Command, Command.State>() {

                @Override
                public void update(Command context, StateChanged stateChanged) {
                    new Node((WebPageEntry)context.getResult());
                    node.expand();
                }
            }).getMenuItem());
            add(new RenameWebPageDialog(node.entry).onCommandFinished(new IStateHandler<Command, Command.State>() {
                @Override
                public void update(Command context, StateChanged stateChanged) {
                    node.updateEntry((WebPageEntry) context.getResult());
                }
            }).getMenuItem());
            add(new DeleteWebPageDialog(node).onCommandFinished(new IStateHandler<Command, Command.State>() {

                @Override
                public void update(Command context, StateChanged stateChanged) {
                    nodes.remove(node.id);
                    model.removeNodeFromParent(node);
                }
            }).getMenuItem());
        }
    }

    public class Node extends DefaultMutableTreeNode {
        public final String id;
        private WebPageEntry entry;
        private String title;

        public Node(WebPageEntry entry) {
            this.entry = entry;
            title = entry.getTitle().getPlainText();
            id = getId(entry);

            if (entry.getParentLink() == null) {
                root.add(this);

                if (waiting.containsKey(id)) {
                    List<Node> nodes = waiting.get(id);
                    waiting.remove(id);

                    for (Node node : nodes)
                        this.add(node);
                }
            } else {
                String parentId = getId(entry.getParentLink().getHref());

                if (nodes.containsKey(parentId))
                    nodes.get(parentId).add(this);
                else {
                    if (!waiting.containsKey(parentId))
                        waiting.put(parentId, new ArrayList<Node>());

                    waiting.get(parentId).add(this);
                }
            }

            nodes.put(id, this);
        }

        private String getId(WebPageEntry entry) {
            return getId(entry.getSelfLink().getHref());
        }

        private String getId(String href) {
            return href.substring(href.lastIndexOf("/") + 1);
        }

        public void expand() {
            expandPath(new TreePath(this.getPath()));
        }

        public void updateEntry(WebPageEntry entry) {
            this.entry = entry;
            title = entry.getTitle().getPlainText();
            model.reload(this);
        }

        @Override
        public String toString() {
            return title;
        }

        public WebPageEntry getEntry() {
            return entry;
        }

        public String getTitle() {
            return title;
        }
    }
}
